package org.dreamwork.cai.srf;

import org.dreamwork.cai.SRFException;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 服务路由表项.
 *
 * <p>这是服务路由表中的最低级描述</p>
 *
 * 每一个 {@code SRI} 描述了一个服务对象，对应于一个 {@code java interface}，在其上提供一个或多个
 * 服务供客户端调用.
 */
public class ServiceRouteItem implements Serializable {
    /** SRI 的名称 */
    public final String name;

    /** 所有的方法描述 */
    public final Set<MethodInfo> methods = Collections.synchronizedSet (new HashSet<> ());

    /** SRI 所属的 SRF */
    public final transient ServiceRouteFrame frame;

    /** SRI 的简短描述 */
    private String description;

    /** SRI 导出的类型 */
    public String[] types;

    /** 所有映射的方法 */
    private final transient Map<String, Object> mapper = Collections.synchronizedMap (new HashMap<> ());

    /** 根据 UUID 映射的加速器 */
    private final transient Map<String, MethodInfo> mappedByUUID = Collections.synchronizedMap (new HashMap<> ());

    /** SRI类型 */
    private transient Class<?>[] javaTypes;

    /**
     * 服务路由条目
     * @param name  条目名称
     * @param frame 条目所属的路由帧
     */
    public ServiceRouteItem (String name, ServiceRouteFrame frame) {
        this.name  = name;
        this.frame = frame;
    }

    /**
     * 向路由条目中添加方法映射
     * @param info 方法
     * @throws SRFException 若添加SRF时有异常将抛出
     */
    @SuppressWarnings ("unchecked")
    public synchronized void add (MethodInfo info) throws SRFException {
        if (mapper.containsKey (info.name)) {
            Object o = mapper.get (info.name);
            if (o instanceof List) {
                List<MethodInfo> list = (List<MethodInfo>) o;
                if (list.contains (info)) {
                    throw new SRFException ("method info: " + info.method.toString () + " already exists");
                }
                list.add (info);
            } else {
                List<MethodInfo> list = new ArrayList<> ();
                list.add ((MethodInfo) o);
                list.add (info);
                mapper.put (info.name, list);
            }
        } else {
            mapper.put (info.name, info);
        }

        methods.add (info);
        mappedByUUID.put (info.uuid, info);
    }

    /**
     * 从路由条目中删除指定的方法映射
     * @param info 方法
     */
    public synchronized void remove (MethodInfo info) {
        if (mapper.containsKey (info.name)) {
            Object o = mapper.get (info.name);
            if (o instanceof MethodInfo) {
                mapper.remove (info.name);
            } else {
                @SuppressWarnings ("unchecked")
                List<MethodInfo> list = (List<MethodInfo>) o;
                list.remove (info);
            }
        }
        methods.remove (info);
        mappedByUUID.remove (info.uuid);
    }

    /**
     * 获取指定 UUID 的方法映射
     * @param uuid 方法映射的 UUID
     * @return 指定 UUID 的方法映射。若未找到，返回 false
     */
    public synchronized MethodInfo get (String uuid) {
        return mappedByUUID.get (uuid);
    }

    /**
     * 根据方法名称和参数类型在路由条目中查找方法映射
     * @param name  方法名称
     * @param types 参数类型
     * @return 匹配的方法映射；若无匹配的方法则返回 null.
     * @see #find(Method)
     */
    public synchronized MethodInfo find (String name, Class<?>... types) {
        if (mapper.containsKey (name)) {
            Object o = mapper.get (name);
            if (o instanceof MethodInfo) {
                return (MethodInfo) o;
            }

            @SuppressWarnings ("unchecked")
            List<MethodInfo> list = (List<MethodInfo>) o;
            for (MethodInfo mi : list) {
                if (matches (types, mi.parameters)) {
                    return mi;
                }
            }
        }

        return null;
    }

    /**
     * 根据 java method 对象查找方法映射
     * @param method java method 对象
     * @return 匹配的方法映射；若无匹配的方法则返回 null
     * @see #find(String, Class[])
     */
    public synchronized MethodInfo find (Method method) {
        return find (method.getName (), method.getParameterTypes ());
    }

    /**
     * 根据指定的参数类型和映射的参数类型进行匹配
     * @param types 方法参数类型
     * @param args  映射的参数类型
     * @return 若匹配返回 true，否在返回 false
     */
    private boolean matches (Class<?>[] types, List<ParameterInfo> args) {
        if (types.length != args.size ()) {
            return false;
        }

        for (int i = 0; i < types.length; i ++) {
            ParameterInfo pi = args.get (i);
            Class<?> type = types [i];
            if (!type.isAssignableFrom (pi.javaType)) {
                return false;
            }
        }

        return true;
    }

    public void setDescription (String description) {
        this.description = description;
    }

    public String getDescription () {
        return this.description;
    }

    public void setTypes (List<Class<?>> types) {
        this.javaTypes = types.toArray (new Class<?>[0]);

        int length = types.size ();
        this.types = new String [length];
        for (int i = 0; i < length; i ++) {
            this.types [i] = javaTypes [i].getCanonicalName ();
        }
    }

    public Class<?>[] getJavaTypes () {
        return javaTypes;
    }
}